package model

import (
	"base"
	"bitset"
	"game_server/logic/example/model"
	"game_server/logic/item/model"
	"game_server/logic/money/model"
	"game_server/logic/player/model"
	"leaf/chanrpc"
	"leaf/log"
	"leaf/timer"
	"sync"
	"time"
)

type Player struct {
	id             base.PlayerId            // id
	gateId         base.AgentId             // 网关服务器Id
	online         bool                     // 是否在线
	loginTimestamp int64                    // 登录时间戳
	dirtyMark      *bitset.BitSet           // 脏标记
	syncMark       map[interface{}]struct{} // 同步标记
	delayCalls     []func()                 // 延时调用
	server         *chanrpc.Server
	timer          *timer.Dispatcher
	functions      chan func()
	logger         *log.Logger

	SaveTimer  timer.Timer
	ExitTimer  timer.Timer
	ExitSignal chan bool // 关闭游戏

	Example *example_model.Example // 例子
	Detail  *player_model.Detail   // 详细信息
	Money   *money_model.Money     // 金钱
	Item    *item_model.Manager    // 道具
}

func NewPlayer(playerId base.PlayerId, gateId base.AgentId, logger *log.Logger) *Player {
	result := new(Player)
	result.id = playerId
	result.gateId = gateId
	result.dirtyMark = bitset.New(1)
	result.syncMark = make(map[interface{}]struct{})
	result.server = chanrpc.NewServer(100)
	result.timer = timer.NewDispatcher(100)
	result.functions = make(chan func(), 100)
	result.logger = logger

	result.ExitSignal = make(chan bool, 1)

	result.init()

	return result
}

func (player *Player) init() {
	player.Example = example_model.NewExample(player.id)
	player.Detail = player_model.NewDetail(player.id)
	player.Money = money_model.NewMoney(player.id)
	player.Item = item_model.NewManager()
}

func (player *Player) Run(group *sync.WaitGroup) {
	for {
		select {
		case <-player.ExitSignal:
			player.server.Close()
			player.timer.Close()
			group.Done()
			return
		case ci := <-player.server.ChanCall:
			player.server.Exec(ci)
			player.executeDelay()
		case ti := <-player.timer.ChanTimer:
			player.timer.Invoking(ti)
		case f := <-player.functions:
			f()
		}
	}
}

func (player *Player) Evoke(id interface{}, args ...interface{}) {
	player.server.Go(id, args...)
}

func (player *Player) Id() base.PlayerId {
	return player.id
}

func (player *Player) GateId() base.AgentId {
	return player.gateId
}

func (player *Player) SetId(playerId base.PlayerId) {
	player.id = playerId
}

func (player *Player) Online() bool {
	return player.online
}

func (player *Player) SetOnline(online bool) {
	player.online = online
}

func (player *Player) LoginTimestamp() int64 {
	return player.loginTimestamp
}

func (player *Player) SetLoginTimestamp(loginTimestamp int64) {
	player.loginTimestamp = loginTimestamp
}

func (player *Player) HasDirtyMark() bool {
	return player.dirtyMark.Any()
}

func (player *Player) SetDirtyMark(mark uint) {
	player.dirtyMark.Set(mark)
}

func (player *Player) ResetDirtyMark() *bitset.BitSet {
	result := player.dirtyMark
	player.dirtyMark = bitset.New(1)
	return result
}

func (player *Player) HasSyncMark(key interface{}) (result bool) {
	if key != nil {
		_, result = player.syncMark[key]
	}

	return
}

func (player *Player) SetSyncMark(key interface{}, mark bool) {
	if key == nil {
		return
	}

	if mark {
		player.syncMark[key] = struct{}{}
	} else {
		delete(player.syncMark, key)
	}
}

func (player *Player) Call(function func()) {
	if function == nil {
		return
	}

	player.functions <- function
}

func (player *Player) AfterCall(d time.Duration, cb func()) timer.Timer {
	return player.timer.AfterCall(d, cb)
}

func (player *Player) LoopCall(interval time.Duration, cb func()) timer.Timer {
	return player.timer.LoopCall(interval, cb)
}

func (player *Player) CronCall(cronExpr *timer.CronExpr, cb func()) timer.Timer {
	return player.timer.CronCall(cronExpr, cb)
}

func (player *Player) StopTimer(t timer.Timer) {
	player.timer.Stop(t)
}

func (player *Player) LogDebug(format string, a ...interface{}) {
	player.logger.Debug(format, a...)
}

func (player *Player) LogRelease(format string, a ...interface{}) {
	player.logger.Release(format, a...)
}

func (player *Player) LogWarning(format string, a ...interface{}) {
	player.logger.Warning(format, a...)
}

func (player *Player) LogError(format string, a ...interface{}) {
	player.logger.Error(format, a...)
}

func (player *Player) LogFatal(format string, a ...interface{}) {
	player.logger.Fatal(format, a...)
}

func (player *Player) DelayCall(call func()) {
	player.delayCalls = append(player.delayCalls, call)
}

func (player *Player) executeDelay() {
	for _, call := range player.delayCalls {
		call()
	}

	player.delayCalls = player.delayCalls[:0]
}
